-
Couldn't load subscription status.
- Fork 452
feat: add experimental AgentConfig with comprehensive tool management #935
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: add experimental AgentConfig with comprehensive tool management #935
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
proposed interface during experimental:
from strands.experimental import config_to_agent
agent = config_to_agent("/path/to/amazing-agent.json")
agent("do the thing")potential interface for non-experimental in future releases:
from strands import config_to_agent
agent = config_to_agent("/path/to/amazing-agent.json")
agent("do the thing")example amazing-agent.json definition of tools:
{
// ...
tools: ["strands_tools.file_read", "my_app.tools.cake_tool", "/path/to/another_tool.py"]
}
strands supports loading tools from python modules and filepaths:
sdk-python/src/strands/tools/registry.py
Lines 57 to 105 in eef11cc
| def add_tool(tool: Any) -> None: | |
| # Case 1: String file path | |
| if isinstance(tool, str): | |
| # Extract tool name from path | |
| tool_name = os.path.basename(tool).split(".")[0] | |
| self.load_tool_from_filepath(tool_name=tool_name, tool_path=tool) | |
| tool_names.append(tool_name) | |
| # Case 2: Dictionary with name and path | |
| elif isinstance(tool, dict) and "name" in tool and "path" in tool: | |
| self.load_tool_from_filepath(tool_name=tool["name"], tool_path=tool["path"]) | |
| tool_names.append(tool["name"]) | |
| # Case 3: Dictionary with path only | |
| elif isinstance(tool, dict) and "path" in tool: | |
| tool_name = os.path.basename(tool["path"]).split(".")[0] | |
| self.load_tool_from_filepath(tool_name=tool_name, tool_path=tool["path"]) | |
| tool_names.append(tool_name) | |
| # Case 4: Imported Python module | |
| elif hasattr(tool, "__file__") and inspect.ismodule(tool): | |
| # Get the module file path | |
| module_path = tool.__file__ | |
| # Extract the tool name from the module name | |
| tool_name = tool.__name__.split(".")[-1] | |
| # Check for TOOL_SPEC in module to validate it's a Strands tool | |
| if hasattr(tool, "TOOL_SPEC") and hasattr(tool, tool_name) and module_path: | |
| self.load_tool_from_filepath(tool_name=tool_name, tool_path=module_path) | |
| tool_names.append(tool_name) | |
| else: | |
| function_tools = self._scan_module_for_tools(tool) | |
| for function_tool in function_tools: | |
| self.register_tool(function_tool) | |
| tool_names.append(function_tool.tool_name) | |
| if not function_tools: | |
| logger.warning("tool_name=<%s>, module_path=<%s> | invalid agent tool", tool_name, module_path) | |
| # Case 5: AgentTools (which also covers @tool) | |
| elif isinstance(tool, AgentTool): | |
| self.register_tool(tool) | |
| tool_names.append(tool.tool_name) | |
| # Case 6: Nested iterable (list, tuple, etc.) - add each sub-tool | |
| elif isinstance(tool, Iterable) and not isinstance(tool, (str, bytes, bytearray)): | |
| for t in tool: | |
| add_tool(t) | |
| else: | |
| logger.warning("tool=<%s> | unrecognized tool specification", tool) |
for the initial implementation in this PR we could do something like:
def config_to_agent(filepath: str) -> Agent:
# pseudo code
parsed_config = JSON.loads(filepath)
tools_list = []
for tool in parsed_config["tools"]:
tool_arg = tool
if not is_filepath(tool):
try:
tool_arg = importlib.import_module(tool)
except ImportError as e:
# handle tool not a file and not a module
tools_list.append(tool_arg)
return Agent(tools=tools_list)832b290 to
f372666
Compare
27bb82f to
c2d1baa
Compare
- Add AgentConfig class for declarative agent configuration via JSON/dict - Support file:// prefix for loading configurations from JSON files - Implement ToolRegistry integration with automatic default tool loading - Add raise_exception_on_missing_tool parameter for flexible error handling - Support tool selection from registry via tool names in config - Add comprehensive test coverage for all configuration scenarios - Move hook events from experimental to production with updated names - Add OpenAI model provider enhancements and Gemini model improvements - Update event loop and tool executors to use production hook events 🤖 Assisted by Amazon Q Developer
- Reset experimental/__init__.py to not import AgentConfig by default - This may resolve import issues in CI environments - AgentConfig can still be imported directly from strands.experimental.agent_config 🤖 Assisted by Amazon Q Developer
- Move JSON schema back to inline variable for simplicity - Add comprehensive tool validation with helpful error messages - Validate tools can be loaded as files, modules, or @tool functions - Add clear documentation about code-based instantiation limitations - Update module docstring and function comments with usage patterns - Add test for tool validation error messages - Remove schemas directory (no longer needed) 🤖 Assisted by Amazon Q Developer
- Fix error message for missing modules to be more descriptive - Remove redundant 'to properly import this tool' text from error messages - Add specific error messages for missing modules vs missing functions - Add unit tests for each error case: - Invalid tool (not file/module/@tool) - Missing module (module doesn't exist) - Missing function (function not found in existing module) - All 17 tests passing with better error coverage 🤖 Assisted by Amazon Q Developer
- Change error message from 'Tool X not found' to 'Module X not found' - More accurate since we're trying to import it as a module at this point - Maintains existing test compatibility and error handling logic 🤖 Assisted by Amazon Q Developer
- Revert previous change from 'Module X not found' back to 'Tool X not found' - Keep original error message format as requested 🤖 Assisted by Amazon Q Developer
da9ff2e to
cb507dd
Compare
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
5f3c0a9 to
64a31ab
Compare
64a31ab to
27cef45
Compare
27cef45 to
9f33f62
Compare
97abdb5 to
6ca10ce
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Some follow-ups:
- Document in code (docstrings) and docs page that it only supports bedrock model ID parameters
- Document using other model providers (change
agent.modelafter transformation) - Track a task for supporting non-bedrock model providers
- Track a task for moving from experimental to production-ready
Feedback addressed
Updated the docs to reflect the model provider specification. I also created a follow up task for supporting more model providers: #1064 Here is the task for moving it out of experimental: #1065 |
Resuming #865
Description
Goal of this feature:
Usage example
For basic usage with the default set of tools available to choose from the config: file_read, editor, http_request, use_agent, and shell
You can also load a config from a file:
Documentation PR
strands-agents/docs#252
Type of Change
New feature
Testing
How have you tested the change? Verify that the changes do not break functionality or introduce warnings in consuming repositories: agents-docs, agents-tools, agents-cli
hatch run prepareChecklist
By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.